{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Partea 2: Introducere în Federated Learning\n",
    "\n",
    "În partea trecută, am învățat despre PointerTensors, care crează infrastructura necesară pentru privacy preserving Deep Learning. În această parte, vom vedea cum să folosim aceste unelte de bază pentru a implementa primul nostru algoritm de privacy preserving deep learning , Federated Learning.\n",
    "\n",
    "Autori:\n",
    "- Andrew Trask - Twitter: [@iamtrask](https://twitter.com/iamtrask)\n",
    "\n",
    "Traducător:\n",
    "- George Muraru: Github [@gmuraru](https://github/com/gmuraru) | Twitter: [@georgemuraru](https://twitter.com/georgemuraru) | Facebook: [@George Cristian Muraru](https://www.facebook.com/georgecmuraru)\n",
    "\n",
    "### Ce este Federated Learning?\n",
    "\n",
    "Este o metodă simplă și puternică pentru a antrena modele pentru Deep Learning. Dacă te gândești la datele pentru antrenare, ele sunt rezultatul unui proces de colectare. Oamenii (prin device-uri) generează informații prin înregistrarea evenimentelor petrecute în lumea reală. Normal, aceaste date sunt agregate într-un singur loc, o locație centrală astfel încât să poți antrena un model de machine learning. Federated Learning face lucrurile exact invers!\n",
    "\n",
    "În loc sa aduci datele pentru antrenare la un model (un server central), vei aduce modelul la date (oriunde se află acesta).\n",
    "\n",
    "Acestă idea permite celui care crează datele să fie singurul care deține o copie permanenta a acestora, și prin aceasta să mențină controlul asupra accesului la date. Destul de mișto, nu?\n",
    "\n",
    "The idea is that this allows whoever is creating the data to own the only permanent copy, and thus maintain control over who ever has access to it. Pretty cool, eh?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Secțiunea 2.1 - Un simple examplu de Federated Learning\n",
    "\n",
    "Să începem antrenând un simplu model folosind metoda centralizată. Inițial vom avea nevoie de:\n",
    "- un dataset simplu\n",
    "- un model\n",
    "- logica de bază pentru antrenare astfel încât să învețe\n",
    "\n",
    "Notă: Dacă acest API nu îți este familiar - mergi la [fast.ai](http://fast.ai) și fă cursul lor înainte de a continua cu acest tutorial."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch\n",
    "from torch import nn\n",
    "from torch import optim"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "# A Toy Dataset\n",
    "data = torch.tensor([[0,0],[0,1],[1,0],[1,1.]], requires_grad=True)\n",
    "target = torch.tensor([[0],[0],[1],[1.]], requires_grad=True)\n",
    "\n",
    "# A Toy Model\n",
    "model = nn.Linear(2,1)\n",
    "\n",
    "def train():\n",
    "    # Logica de antrenare\n",
    "    opt = optim.SGD(params=model.parameters(),lr=0.1)\n",
    "    for iter in range(20):\n",
    "\n",
    "        # 1) șterge gradienții din trecut (dacă aceștia există)\n",
    "        opt.zero_grad()\n",
    "\n",
    "        # 2) fă o predicție\n",
    "        pred = model(data)\n",
    "\n",
    "        # 3) calculează cât de mult ne-am înșelat\n",
    "        loss = ((pred - target)**2).sum()\n",
    "\n",
    "        # 4) verifică care ponderi ne fac să ne înșelăm\n",
    "        loss.backward()\n",
    "\n",
    "        # 5) modifică aceste ponderi\n",
    "        opt.step()\n",
    "\n",
    "        # 6) afișează progresul\n",
    "        print(loss.data)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "train()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Și cam asta este! Am antrenat un model simplu în modul convențional. Toate datele sunt agregate pe mașina noastră locală și le putem folosi pentru a updata modelul. Federated Learning, totuși, nu funcționează așa. Așa că hai să modificăm acest exemplu pentru a folosi metoda de Federated Learning!\n",
    "\n",
    "Așadar, de ce vom avea nevoie:\n",
    "- crearea câțiva workeri\n",
    "- pointeri pentru datele folosite la antrenare aflat la fiecare worker\n",
    "- modificarea logicii de antrenare pentru federated learning\n",
    "\n",
    "    Noi pași de antrenare:\n",
    "    - trimiterea modelului la worker\n",
    "    - antrenarea pe datele aflate acolo\n",
    "    - recuperează modelul și repetă pentru următorul worker"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import syft as sy\n",
    "hook = sy.TorchHook(torch)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# crează câțiva workeri\n",
    "\n",
    "bob = sy.VirtualWorker(hook, id=\"bob\")\n",
    "alice = sy.VirtualWorker(hook, id=\"alice\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Un dataset simplu\n",
    "data = torch.tensor([[0,0],[0,1],[1,0],[1,1.]], requires_grad=True)\n",
    "target = torch.tensor([[0],[0],[1],[1.]], requires_grad=True)\n",
    "\n",
    "# obține pointeri la datele folosite pentru antrenare\n",
    "# aflate la fiecare worker prin trimiterea datelor la\n",
    "# bob și alice\n",
    "data_bob = data[0:2]\n",
    "target_bob = target[0:2]\n",
    "\n",
    "data_alice = data[2:]\n",
    "target_alice = target[2:]\n",
    "\n",
    "# inițializează un model simplu\n",
    "model = nn.Linear(2,1)\n",
    "\n",
    "data_bob = data_bob.send(bob)\n",
    "data_alice = data_alice.send(alice)\n",
    "target_bob = target_bob.send(bob)\n",
    "target_alice = target_alice.send(alice)\n",
    "\n",
    "# organizează pointerii într-o listă\n",
    "datasets = [(data_bob,target_bob),(data_alice,target_alice)]\n",
    "\n",
    "opt = optim.SGD(params=model.parameters(),lr=0.1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def train():\n",
    "    # Logica de antrenare\n",
    "    opt = optim.SGD(params=model.parameters(),lr=0.1)\n",
    "    for iter in range(10):\n",
    "        \n",
    "        # NOU) itereaza prin setul de date aflat la fiecare worker\n",
    "        for data,target in datasets:\n",
    "            \n",
    "            # NOU) trimite modelul la worker-ul corect\n",
    "            model.send(data.location)\n",
    "\n",
    "            # 1) șterge gradieții din trecut (dacă aceștia există)\n",
    "            opt.zero_grad()\n",
    "\n",
    "            # 2) fă o predicție\n",
    "            pred = model(data)\n",
    "\n",
    "            # 3) calculează cât de mult ne-am înșelat\n",
    "            loss = ((pred - target)**2).sum()\n",
    "\n",
    "            # 4) verifică care ponderi ne fac să ne înșelăm\n",
    "            loss.backward()\n",
    "\n",
    "            # 5) modifică aceste ponderi\n",
    "            opt.step()\n",
    "            \n",
    "            # NOU) recuperează modelul (cu noii gradienți)\n",
    "            model.get()\n",
    "\n",
    "            # 6) afișează progresul\n",
    "            print(loss.get()) # NOU) ușoară modificare ... trebuie să apelăm .get() pe loss\n",
    "    \n",
    "# federated averaging"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "train()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Bună treabă!\n",
    "\n",
    "Și voilà! Acum antrenăm un model foarte simplu de Deep Learning folosind Federated Learning! Trimited modelul la fiecare worker, generăm un nou gradient, și apoi recuperăm gradientul unde ne updatăm modelul global. Niciodată în acest proces nu vedem sau cerem acces la datele folosite pentru antrenare! lui Bob Și Alice!!! Păstrăm confidențiale datele lui Bob și Alice!!!\n",
    "\n",
    "### Dezavantaje ale acestui Exemplu\n",
    "\n",
    "Chiar dacă acest exemplu este drăguț pentru o introducere în Federated Learning, încă are câteva dezavantaje majore. Cel mai notabil, când apelăm `model.get()` și primim noul model de la Bob sau Alice, putem să despre datele folosite la antrenare ale lui Bob sau Alice prin simpla observare a gradientului. În anumite cazuri, putem să refacem datele de antrenare complet!\n",
    "\n",
    "Așadar, ce se poate face? Ei bine, prima strategie folosită de oameni este **să facă media aritmetică a gradientului înainte ca acesta să fie uploadat la serverul central**. Cu toate astea, această strategie necesită folosirea mai sofisticată a obiectelor de tipul PointerTensor. Așadar, în următoarea secțiune, ne vom rezerva ceva timp pentru a învăța despre tehnici mai avansate și apoi vom updata acest exemplu de Federated Learning. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Felicitări!!! - Este timpul să te alături comunității!\n",
    "\n",
    "Felicitări pentru completarea tutorialului! Daca ți-a făcut plăcere și ai dori să faci parte din \n",
    "Congratulations on completing this notebook tutorial! If you enjoyed this and would like to join the movement toward privacy preserving, decentralized ownership of AI and the AI supply chain (data), you can do so in the following ways!\n",
    "\n",
    "### Oferă o stea repo-ului PySyft pe GitHub\n",
    "\n",
    "Cea mai ușoară metodă de a ajuta comunitatea este de a oferi o \"steluță\" repo-urilor de pe GitHub! Acest lucru ajută la acumularea de notorietate în ceea ce privesc tool-urile cool la care lucrăm.\n",
    "\n",
    "- [Star PySyft](https://github.com/OpenMined/PySyft)\n",
    "\n",
    "### Alaturăte comunității pe Slack!\n",
    "\n",
    "Cea mai bună metodă de a fi la curent cu ultimele progrese este să te alături comunității noastre. Poți face acest lucru prin completarea formularului de la [http://slack.openmined.org](http://slack.openmined.org)\n",
    "\n",
    "### Alătură-te unui proiect!\n",
    "\n",
    "Cea mai bună metodă de a contribui este de a deveni un contributor activ (de a scrie cod și crea pull request-uri - PR-uri). În orice moment poți merge pe pagina GitHub, cu Issues, a proiectului, și să filtrezi după \"Projects\". Asta îți va arăta Ticketele \"generale\" și îți va oferi o privire de ansamblu despre proiectele la care poți participa. Dacă nu dorești să te alături unui proiect, dar dorești să scrii câteva linii de cod, poți să cauți \"mini-proiecte\" prin căutarea tagului de \"good first issue\".\n",
    "\n",
    "- [PySyft Projects](https://github.com/OpenMined/PySyft/issues?q=is%3Aopen+is%3Aissue+label%3AProject)\n",
    "- [Good First Issue Tickets](https://github.com/OpenMined/PySyft/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n",
    "\n",
    "### Donează\n",
    "\n",
    "Dacă nu ai timp să contribui la codebase, dar dorești să îți arăți suportul, se poate, de asemenea, să devii un Backer pentru Open Collective. Toate donațiile merg spre hosting-ului paginii web și alte cheltuieli ale comunității, cum ar fi hackathoane și întâlniri!\n",
    "\n",
    "[OpenMined's Open Collective Page](https://opencollective.com/openmined)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
